package org.yinxianren.java.test.thread;

/**
 * 在这个指南中，我们将学习在Java中如何使用一个最基本的同步方法，即使用 synchronized关键字来控制并发访问方法。
 * 只有一个执行线程将会访问一个对象中被synchronized关键字声明的方法。
 * 如果另一个线程试图访问同一个对象中任何被synchronized关键字声明的方法，它将被暂停，直到第一个线程结束方法的执行。
 * 换句话说，每个方法声明为synchronized关键字是一个临界区，Java只允许一个对象执行其中的一个临界区。
 * 静态方法有不同的行为。只有一个执行线程访问被synchronized关键字声明的静态方法，
 * 但另一个线程可以访问该类的一个对象中的其他非静态的方法。 你必须非常小心这一点，
 * 因为两个线程可以访问两个不同的同步方法，如果其中一个是静态的而另一个不是。
 * 如果这两种方法改变相同的数据,你将会有数据不一致 的错误。
 * 为了学习这个概念，我们将实现一个有两个线程访问共同对象的示例。我们将有一个银行帐户和两个线程：
 * 其中一个线程将钱转移到帐户而另一个线程将从账户中扣款。在没有同步方法，我们可能得到不正确的结果。
 * 同步机制保证了账户的正确。
 */
public class DemoSynchronized {
    public static void main(String[] args) {
        Account account=new Account();
        account.setBalance(1000);

        Company company=new Company(account);
        Thread companyThread=new Thread(company);

        Bank bank=new Bank(account);
        Thread bankThread=new Thread(bank);

        System.out.printf("Account : Initial Balance: %f\n",account.getBalance());

        companyThread.start();
        bankThread.start();

        try {
            companyThread.join();
            bankThread.join();
            System.out.printf("Account : Final Balance: %f\n",account.getBalance());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
/*
在这个指南中，你已经开发了一个增加和减少模拟银行账户的类的余额的应用程序。
在这个程序中，每次都调用100次addAmount()方法来增加1000 的余额和调用100次subtractAmount()方法来减少1000的余额。
你应该期望最终的余额和初始的余额是相等的。你试图促使一个错误情况使 用tmp变量来存储账户余额，所以你读取帐户余额,
你增加临时变量的值，然后你再次设置账户的余额值。另外，你通过使用Thread类的sleep()方 法引入一个小延迟，
让执行该方法的线程睡眠10毫秒，所以，如果另一个线程执行该方法,它可以修改账户的余额来引发一个错误。
这是 synchronized关键字机制，避免这些错误。

如果你想看到并发访问共享数据的问题，那么就删除addAmount()和 subtractAmount()方法的synchronized关键字，
然后运行该程序。在没有synchronized关键字的情况下，当一个线程在 睡眠后再读取账户的余额，另一个方法将读取该账户的余额。
所以这两个方法将修改相同的余额并且其中一个操作不会反映在最终的结果。
 */
class Account {
    private double balance;

    public double getBalance() {
        return balance;
    }

    public void setBalance(double balance) {
        this.balance = balance;
    }

    public
         synchronized
            void addAmount(double amount) {
        double tmp=balance;
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        tmp+=amount;
        balance=tmp;
    }


    public
        synchronized
                  void subtractAmount(double amount) {
        double tmp=balance;
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        tmp-=amount;
        balance=tmp;
    }
}
class Bank implements Runnable {

    private Account account;
    public Bank(Account account) {
        this.account=account;
    }

    @Override
    public void run() {
        for (int i=0; i<100; i++){
            account.subtractAmount(1000);
        }
    }
}

class Company implements Runnable {

    private Account account;
    public Company(Account account) {
        this.account=account;
    }

    @Override
    public void run() {
        for (int i=0; i<100; i++){
            account.addAmount(1000);
        }
    }
}